import { Show } from "@/component/Display";
import { GlobalValue } from "@/GlobalValue";
import { Notificatorio } from "@/hooks/Providers";
import { useRefState } from "@/hooks/useRefState";
import { call_native } from "@/utils/call_native";
import { Poller } from "@/utils/Poller";
import { LoadingOutlined, ReloadOutlined } from "@ant-design/icons";
import { Button, Descriptions, Flex, Input, InputRef, Modal, ModalProps, Select, Space, Tooltip, Typography } from "antd";
import { DescriptionsItemType } from "antd/es/descriptions";
import { DefaultOptionType } from "antd/es/select";
import { TextProps } from "antd/es/typography/Text";
import axios from "axios";
import localforage from "localforage";
import { ReactNode, useEffect, useMemo, useRef, useState } from "react";
import { useImmer } from "use-immer";
import ApiV1WxAccountSave_post from "../../api/ApiV1WxAccountSave_post"; // TAV
import styles from "./ChatlogSettingsModal.module.scss";
import { useLoginInfo } from "@/hooks/useLoginInfo";


export function Text(props: TextProps) {
  return (
    <Tooltip title={props.title || props.children}>
      <Typography.Text {...props} style={{ overflow: 'hidden', textOverflow: 'ellipsis', maxWidth: '100%', whiteSpace: 'nowrap' }} />
    </Tooltip>
  )
}
export function ChatlogSettingsModal(props: ModalProps) {
  const { ..._p } = props;
  const ref_progress = useRef<HTMLSpanElement>(null);
  const progress = useMemo(() => ({
    get text(): string { return progress.text ?? '' },
    set text(v: string) { if (ref_progress.current) ref_progress.current.innerText = v }
  }), [])
  const [is_native_ready, set_is_native_ready, ref_is_native_ready] = useRefState(false);
  const [pending, set_pending, ref_pending] = useRefState(false);
  const [loading, set_loading, ref_loading] = useRefState(false);
  const phone = useLoginInfo<string>('mobile')
  const [belong_submitting, set_belong_submitting] = useState(false);
  const [belong_info, set_belong_info] = useImmer<{ open?: boolean, data?: ApiV1WxAccountSave_post.RespData }>({})
  const [{ chatlog_state: { http_addr, http_enabled } = {} }, set_global_value] = GlobalValue.use()
  const [state, set_state] = useImmer<GlobalValue.IChatlogState>({})
  const desc_items = useMemo(() => {
    const desc_items: (DescriptionsItemType | undefined)[] = []
    const account_select = (
      <Select
        size='small'
        variant='borderless'
        value={state.account}
        loading={loading}
        suffixIcon={null}
        className={styles.short_select}
        options={state.wx_instances?.map<DefaultOptionType>(v => {
          return {
            value: v.name,
            label: <>
              {v.name}
              <Text type='secondary'>
                [{v.fullVersion}]
              </Text>
            </>,
            title: JSON.stringify(v)
          }
        })} placeholder='切换账号' />
    )
    const m = { lg: 6, md: 8, sm: 12, xs: 24 }
    const is_wx_open = state.pid;
    const is_wx_login = !!state.account
    const item = (label: ReactNode, value: ReactNode, fallback?: ReactNode) => {
      const c = value || fallback
      return c ? {
        label, span: m,
        children: <Text>{c}</Text>
      } : void 0
    }
    const filled = (label: ReactNode, value: ReactNode, fallback?: ReactNode) => {
      const c = value || fallback
      return c ? {
        label, span: 'filled' as const,
        children: <Text>{c}</Text>
      } : void 0
    }
    desc_items.push(
      item(`Account`, account_select),
      item(`PID`, state.pid),
      item(`Platform`, state.platform),
      item(`Version`, state.fullVersion),
      item(`Status`, is_wx_open ? state.status : void 0),
      item(`PID`, state.pid),
      filled(`ExePath`, state.exePath),
      item(`Data Usage`, state.data_usage),
      filled(`Data Dir`, state.data_dir),
      item(`Work Usage`, state.work_usage),
      filled(`Work Dir`, state.work_dir),
      item(`Auto Decrypt`, typeof state.auto_decrypt !== 'boolean' ? void 0 : state.auto_decrypt ? '已开启' : '未开启'),
      filled(`Http Addr`, typeof state.http_enabled !== 'boolean' ? void 0 : !state.http_enabled ? `[未启动]` : <><Text type='success'>[已启动]</Text> {state.http_addr}</>),
      filled(`Data Key`, state.data_key),
      filled(`Img Key`, state.img_key),

    )
    return desc_items.filter(Boolean) as DescriptionsItemType[];
  }, [state])

  useEffect(() => {
    set_global_value('chatlog_state', state)
  }, [state])

  useEffect(() => {
    const forage_key = 'account_info'
    if (!http_addr) {
      localforage.removeItem(forage_key).catch(e => { })
      return;
    }
    const poller = new Poller(async () => {
      const { data } = await axios(`http://${http_addr}/api/v1/info`, { responseType: 'json', timeout: 1000 });
      if (!is_native_ready) {
        await localforage.removeItem(forage_key).catch(e => { })
        set_state({
          data_usage: data.dataUsage,
          data_dir: data.dataDir,
          work_usage: data.workUsage,
          work_dir: data.workDir,
          auto_decrypt: data.autoDecrypt,
          http_enabled: true,
          http_addr: http_addr,
          account: data.account,
          status: data.status,
          pid: data.pid,
          exePath: data.exePath,
          fullVersion: data.fullVersion,
          platform: data.platform,
          img_key: data.imgKey,
          data_key: data.dataKey,
        })
      }
      if (!data.account) throw new Error('not ready');
      const info = await localforage.getItem(forage_key).catch(e => { null })
      if (info) return;
      const body: ApiV1WxAccountSave_post.BodyParams = {
        account: data.account,
        platform: data.platform,
        version: data.fullVersion,
        status: data.status,
      }
      const result = await ApiV1WxAccountSave_post.call(body).then(r => r.data).catch(e => console.warn('' + e))
      if (!result) return;
      await localforage.setItem(forage_key, result);
      if (!result.phone) set_belong_info({ open: true, data: result })
    })

    poller.debug_id = '' + Date.now()
    poller.interval = 1000;
    poller.start();
    (window as any).poller = poller
    return () => poller.stop();
  }, [http_addr, is_native_ready])

  const [notificatorio] = Notificatorio.use()
  const start_up = () => {
    if (ref_loading.current) return;
    if (!call_native.obj()) return;
    set_loading(true);

    const job = async () => {
      progress.text = 'starting....'
      set_state({});
      const is_ready = await call_native("GetIsChatlogReady").then(r => r.data).catch(e => false);
      set_is_native_ready(is_ready)
      if (!is_ready) {
        const no_console = !document.body.classList.contains('dev')
        await call_native('StartChatlog', no_console).then(() => set_is_native_ready(is_ready))
      }

      progress.text = 'GetDataKey....'
      let data_key = await call_native("GetDataKey").then(r => r.data).catch(e => '')
      progress.text = 'GetImgKey....'
      let img_key = await call_native("GetImgKey").then(r => r.data).catch(e => '')
      if (!data_key || !img_key) {
        progress.text = 'SearchKeys....'
        await call_native("SearchKeys").then(r => {
          data_key = r.data.data_key;
          img_key = r.data.img_key;
        }).catch(e => { })
      }

      set_state(o => {
        o.data_key = data_key;
        o.img_key = img_key;
      })

      progress.text = 'GetWorkDir....'
      let work_dir = await call_native("GetWorkDir").then(r => r.data).catch(e => void 0)
      if (!work_dir) {
        progress.text = 'DecryptDBFiles....'
        await call_native("DecryptDBFiles").then(r => work_dir = r.data)
      }
      progress.text = 'GetHTTPEnabled....'
      let http_enabled = await call_native("GetHTTPEnabled").then(r => r.data).catch(e => void 0)
      if (!http_enabled) {
        progress.text = 'StartService....'
        await call_native("StartService").then(() => http_enabled = true)
      }
      progress.text = 'GetAutoDecrypt....'
      let auto_decrypt = await call_native("GetAutoDecrypt").then(r => r.data).catch(e => void 0)
      if (!auto_decrypt) {
        progress.text = 'StartAutoDecrypt....'
        await call_native("StartAutoDecrypt").then(() => auto_decrypt = true)
      }

      progress.text = 'GetAccount....'
      const account = await call_native("GetAccount").then(r => r.data).catch(e => void 0)
      progress.text = 'GetWXInstances....'
      const wx_instances = await call_native("GetWXInstances").then(r => r.data).catch(e => void 0)
      const instance = wx_instances?.find(v => v.name === account)
      const state: GlobalValue.IChatlogState = {
        account: account,
        wx_instances,
        data_usage: await call_native("GetDataUsage").then(r => r.data).catch(e => void 0),
        data_dir: await call_native("GetDataDir").then(r => r.data).catch(e => void 0),
        work_usage: await call_native("GetWorkUsage").then(r => r.data).catch(e => void 0),
        work_dir,
        auto_decrypt,
        http_enabled,
        http_addr: await call_native("GetHTTPAddr").then(r => r.data).catch(e => void 0),
        status: await call_native("GetWXStatus").then(r => r.data).catch(e => void 0),
        platform: await call_native("GetPlatform").then(r => r.data).catch(e => void 0),
        fullVersion: await call_native("GetWXFullVersion").then(r => r.data).catch(e => void 0),
        pid: '' + instance?.pid,
        exePath: '' + instance?.exePath,
        cur_wx: instance,
      }

      const ready = await call_native("GetIsChatlogReady").then(r => r.data).catch(e => false);
      set_is_native_ready(ready)
      set_state(o => { Object.assign(o, state) })
    }

    job().catch(e => {
      notificatorio.error({
        key: '' + e,
        message: '' + e
      })
    }).finally(() => {
      progress.text = ''
      set_loading(false)
    })
  }

  useEffect(() => {
    if (!!call_native) start_up()
  }, [])

  const refresh = () => {
    if (ref_loading.current) return;
    if (!call_native.obj()) return;
    set_loading(true);
    const job = async () => {
      progress.text = 'Refresh....'
      const is_ready = await call_native("GetIsChatlogReady").then(r => r.data).catch(e => false);
      set_is_native_ready(is_ready)
      if (!is_ready) return set_state({});
      progress.text = 'GetDataKey....'
      const data_key = await call_native("GetDataKey").then(r => r.data).catch(e => '')
      progress.text = 'GetImgKey....'
      const img_key = await call_native("GetImgKey").then(r => r.data).catch(e => '')
      const http_enabled = await call_native("GetHTTPEnabled").then(r => r.data).catch(e => void 0)
      const work_dir = await call_native("GetWorkDir").then(r => r.data).catch(e => void 0)
      const auto_decrypt = await call_native("GetAutoDecrypt").then(r => r.data).catch(e => void 0)
      const account = await call_native("GetAccount").then(r => r.data).catch(e => void 0)
      const wx_instances = await call_native("GetWXInstances").then(r => r.data).catch(e => void 0)
      const instance = wx_instances?.find(v => v.name === account)
      const state: GlobalValue.IChatlogState = {
        account,
        data_key,
        img_key,
        wx_instances,
        data_usage: await call_native("GetDataUsage").then(r => r.data).catch(e => void 0),
        data_dir: await call_native("GetDataDir").then(r => r.data).catch(e => void 0),
        work_usage: await call_native("GetWorkUsage").then(r => r.data).catch(e => void 0),
        work_dir,
        auto_decrypt,
        http_enabled,
        http_addr: await call_native("GetHTTPAddr").then(r => r.data).catch(e => void 0),
        status: await call_native("GetWXStatus").then(r => r.data).catch(e => void 0),
        platform: await call_native("GetPlatform").then(r => r.data).catch(e => void 0),
        fullVersion: await call_native("GetWXFullVersion").then(r => r.data).catch(e => void 0),
        pid: '' + instance?.pid,
        exePath: '' + instance?.exePath,
        cur_wx: instance,
      }
      set_state(o => { Object.assign(o, state) })
    }
    job().catch(e => {
      notificatorio.error({
        key: '' + e,
        message: '' + e
      })
    }).finally(() => {
      progress.text = ''
      set_loading(false)
    })
  }
  const [refresh_flag, set_refresh_flag] = useState(0)
  useEffect(() => {
    if (props.open) set_refresh_flag(v => ++v)
  }, [props.open])

  useEffect(() => {
    refresh()
  }, [refresh_flag])

  const title_row = (
    <Space style={{ userSelect: 'none' }}>
      Chatlog Settings
      <Text type='secondary'>
        <span ref={ref_progress} />
      </Text>
      <Show yes={loading || pending}>
        <LoadingOutlined />
      </Show>
      <Show yes={is_native_ready && !loading && !pending}>
        <ReloadOutlined onClick={() => refresh()} title="刷新" />
      </Show>
    </Space>
  )
  const ref_input_http_addr = useRef<InputRef>(null)
  const width = `min(calc(100% - 100px),1000px)`
  return <>
    <Modal title={title_row} footer={null} width={width} {..._p} forceRender>
      <Flex wrap gap={10} vertical style={{ overflowX: 'hidden' }}>
        <Show yes={!call_native.obj()}>
          <Flex gap={5} align='center' justify='flex-start'>
            <Space.Compact>
              <Select
                disabled={loading || pending || http_enabled}
                size='small'
                options={[{ value: "http" }]}
                value='http'
                suffixIcon={null} style={{ minWidth: 60 }} />
              <Input
                ref={ref_input_http_addr}
                disabled={loading || pending || http_enabled}
                size='small'
                defaultValue={'127.0.0.1:5030'} />
              <Button disabled={loading || pending} size='small' type='primary'
                onClick={() => {
                  if (http_enabled) {
                    set_global_value("chatlog_state", { http_addr: void 0, http_enabled: false })
                    set_state({})
                    return
                  }
                  const http_addr = '' + ref_input_http_addr.current?.input?.value;
                  set_pending(true);
                  axios(`http://${http_addr}`).then((r) => {
                    if (r.status < 200 || r.status > 299)
                      throw new Error('无法连接至chatlog，是否未启动相关服务？')
                    notificatorio.success({ message: 'ok' })
                    set_global_value("chatlog_state", { http_addr, http_enabled: true })
                  }).catch(e => {
                    notificatorio.error({
                      key: '' + e, message: <Space direction='vertical'>
                        <>无法连接至chatlog，是否未启动相关服务？</>
                        <>{'' + e}</>
                      </Space>
                    })
                  }).finally(() => {
                    set_pending(false)
                  })
                }}>
                {http_enabled ? "断开" : "连接"}
              </Button>
            </Space.Compact>
          </Flex>
        </Show>
        <Show yes={call_native.obj()}>
          <Flex wrap gap={5} onClick={e => e.stopPropagation()} >
            <Button
              size='small'
              title={is_native_ready ? '关闭Chatlog' : '启动Chatlog'}
              disabled={loading || pending}
              type='primary'
              onClick={() => {
                if (!ref_is_native_ready.current)
                  return start_up()
                if (ref_pending.current) return;
                set_pending(true)

                return call_native('StopChatlog')
                  .then(() => set_refresh_flag(v => ++v))
                  .catch(e => notificatorio.error({ key: '' + e, message: '' + e }))
                  .finally(() => set_pending(false))

              }}>
              {is_native_ready ? '关闭Chatlog' : '启动Chatlog'}
            </Button>
            <Button
              size='small'
              title="从进程获取数据密钥"
              disabled={loading || pending || !is_native_ready}
              onClick={() => {
                if (ref_pending.current) return;
                set_pending(true)
                call_native("GetDataKey")
                  .then(() => set_refresh_flag(v => ++v))
                  .catch(e => notificatorio.error({ key: '' + e, message: '' + e }))
                  .finally(() => set_pending(false))
              }}>
              获取数据密钥
            </Button>
            <Button
              size='small'
              title="从进程获取数据密钥"
              disabled={loading || pending || !is_native_ready}
              onClick={() => {
                if (ref_pending.current) return;
                set_pending(true)
                call_native("DecryptDBFiles")
                  .then(() => set_refresh_flag(v => ++v))
                  .catch(e => notificatorio.error({ key: '' + e, message: '' + e }))
                  .finally(() => set_pending(false))
              }}>
              解密数据
            </Button>
            <Button
              size='small'
              title={state.auto_decrypt ? '停止监控数据目录更新，不再自动解密新增数据' : '监控数据目录更新，自动解密新增数据'}
              disabled={loading || pending || !is_native_ready}
              onClick={() => {
                if (ref_pending.current) return;
                set_pending(true)
                call_native(state.auto_decrypt ? 'StopAutoDecrypt' : 'StartAutoDecrypt')
                  .then(() => refresh())
                  .catch(e => notificatorio.error({ key: '' + e, message: '' + e }))
                  .finally(() => set_pending(false))
              }}>
              {state.auto_decrypt ? '停止自动解密' : '开启自动解密'}
            </Button>
            <Button
              size='small'
              title={state.auto_decrypt ? '停止本地 HTTP & MCP 服务器' : '启动本地 HTTP & MCP 服务器'}
              disabled={loading || pending || !is_native_ready}
              onClick={() => {
                if (ref_pending.current) return;
                call_native(state.http_enabled ? 'StopService' : 'StartService')
                  .then(() => set_refresh_flag(v => ++v))
                  .catch(e => notificatorio.error({ key: '' + e, message: '' + e }))
                  .finally(() => set_pending(false))
              }
              }>
              {state.http_enabled ? '停止HTTP服务' : '启动HTTP服务'}
            </Button>
            <Space.Compact>
              <Select disabled={loading || pending || is_native_ready} size='small' options={[{ value: "tcp" }]} value='tcp' suffixIcon={null} style={{ minWidth: 60 }} />
              <Input readOnly disabled={loading || pending || is_native_ready} size='small' defaultValue={'127.0.0.1:1234'} />
              <Button disabled={loading || pending || is_native_ready} size='small' type='primary'
                onClick={() => {
                  if (ref_pending.current) return;
                  set_pending(true);
                  call_native('ConnectChatlog', 'tcp', '127.0.0.1:1234')
                    .then(() => set_refresh_flag(v => ++v))
                    .catch(e => notificatorio.error({ key: '' + e, message: '' + e }))
                    .finally(() => set_pending(false))
                }}>
                连接
              </Button>
            </Space.Compact>
          </Flex>
        </Show>
        <Show yes={!!(call_native.obj() || state)}>
          <Descriptions
            items={desc_items}
            column={24}
            size='small' />
        </Show>
      </Flex>
    </Modal>
    <Modal
      title={
        <Flex vertical>
          <span>当前微信未设置手机号</span>
          <span>是否设置为{phone}?</span>
        </Flex>
      }
      open={belong_info.open && !!phone}
      afterClose={() => set_belong_info({})}
      onCancel={() => set_belong_info(o => { o.open = false })}
      maskClosable={false}
      closeIcon={null}
      cancelButtonProps={{
        disabled: belong_submitting,
      }}
      okButtonProps={{
        disabled: belong_submitting,
        loading: belong_submitting
      }}
      onOk={() => {
        set_belong_submitting(true);
        ApiV1WxAccountSave_post.call({ ...belong_info.data, phone: phone! }).then(r => {
          set_belong_info(o => { o.open = false })
        }).finally(() => {
          set_belong_submitting(false)
        })
      }}>
      <></>
    </Modal>
  </>
}